package com.ssyx.order.service.Impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ssyx.common.auth.AuthContextHolder;
import com.ssyx.common.exception.SsyxException;
import com.ssyx.common.result.ResultCodeEnum;
import com.ssyx.enums.*;
import com.ssyx.model.activity.ActivityRule;
import com.ssyx.model.activity.CouponInfo;
import com.ssyx.model.order.CartInfo;
import com.ssyx.model.order.OrderInfo;
import com.ssyx.model.order.OrderItem;
import com.ssyx.order.feign.ActivityFeignClient;
import com.ssyx.order.feign.CartFeignClient;
import com.ssyx.order.feign.ProductFeignClient;
import com.ssyx.order.feign.UserFeignClient;
import com.ssyx.order.mapper.OrderInfoMapper;
import com.ssyx.order.mapper.OrderItemMapper;
import com.ssyx.order.service.OrderInfoService;
import com.ssyx.order.service.OrderItemService;
import com.ssyx.utils.DateUtil;
import com.ssyx.vo.order.CartInfoVo;
import com.ssyx.vo.order.OrderConfirmVo;
import com.ssyx.vo.order.OrderSubmitVo;
import com.ssyx.vo.order.OrderUserQueryVo;
import com.ssyx.vo.product.SkuStockLockVo;
import com.ssyx.vo.user.LeaderAddressVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * ClassName:OrderInfoServiceImpl
 * Description:
 *
 * @Author kay
 * @Create 2024/5/22 9:12
 * @Version 1.0
 */
@Service
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo>
        implements OrderInfoService {

    @Autowired
    private UserFeignClient userFeignClient;

    @Autowired
    private CartFeignClient cartFeignClient;

    @Autowired
    private ActivityFeignClient activityFeignClient;

    @Autowired
    private ProductFeignClient productFeignClient;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private OrderItemService orderItemService;

    //确认订单
    @Override
    public OrderConfirmVo confirmOrder() {
        //获取当前用户id
        Long userId = AuthContextHolder.getUserLoginVo().getUserId();

        //根据用户id获取团长信息和地址
        LeaderAddressVo leaderAddressVo = userFeignClient.getUserAddressByUserId(userId);

        //根据用户id得到,购物车中被选中的商品项列表
        List<CartInfo> cartCheckedList = cartFeignClient.getCartCheckedList(userId);

        //自定义生成订单号,防重复,保存在redis中一份
        String orderNo = System.currentTimeMillis()+"";
        redisTemplate.opsForValue().set("user:"+userId+"orderNo:",orderNo,1, TimeUnit.DAYS);

        //获取购物车满足条件的促销与优惠券信息
        OrderConfirmVo orderConfirmVo = activityFeignClient.findCartActivityAndCoupon(cartCheckedList, userId);
        orderConfirmVo.setOrderNo(orderNo);
        orderConfirmVo.setLeaderAddressVo(leaderAddressVo);
        return orderConfirmVo;
    }

    //生成订单
    @Override
    public Long submitOrder(OrderSubmitVo orderSubmitVo) {
        //第一步,得到用户id,
        Long userId = AuthContextHolder.getUserLoginVo().getUserId();
        orderSubmitVo.setUserId(userId);

        //第二步,防止订单重复提交
        //// 通过redis+lua脚本进行判断 订单号orderNo存在redis中的,key:"user:"+userId+"orderNo:"  value:orderNo
        String orderNo = orderSubmitVo.getOrderNo();
        if (StringUtils.isEmpty(orderNo)){
            throw new SsyxException(ResultCodeEnum.ILLEGAL_REQUEST);
        }
        ////获取传递过来的orderNo,到redis中查询,如果redis中有相同的orderNo,表示正常提交订单,然后删除orderNo
        String script = "if(redis.call('get', KEYS[1]) == ARGV[1]) " +
                "then return redis.call('del', KEYS[1]) else return 0 end";
        Boolean flag = (Boolean)redisTemplate.execute(new DefaultRedisScript(script, Boolean.class),
                                                      Arrays.asList("user:"+userId+"orderNo:"), orderNo);

        ////如果redis中没有相同的orderNo,则表示重复提交
        if (!flag){
            throw new SsyxException(ResultCodeEnum.REPEAT_SUBMIT);
        }
        //第三步 验证库存,并且锁定库存 Redisson分布式锁
        ////获取当前用户购物车中选中的商品,在此方法中就代表订单商品列表
        List<CartInfo> cartInfoList = cartFeignClient.getCartCheckedList(userId);
        ////通过stream流过滤出普通商品(秒杀商品,本项目不做处理)
        List<CartInfo> commonSkuList = cartInfoList.stream()
                        .filter(cartInfo -> cartInfo.getSkuType() == SkuType.COMMON.getCode())
                        .collect(Collectors.toList());
        //List<CartInfo> -> List<SkuStockLockVo> 转换后方便 锁定商品库存
        if(!CollectionUtils.isEmpty(commonSkuList)) {
            List<SkuStockLockVo> commonStockLockVoList = commonSkuList.stream().map(item -> {
                SkuStockLockVo skuStockLockVo = new SkuStockLockVo();
                skuStockLockVo.setSkuId(item.getSkuId());
                skuStockLockVo.setSkuNum(item.getSkuNum());
                return skuStockLockVo;
            }).collect(Collectors.toList());

            //是否锁定(普通商品)
            Boolean isLockCommon = productFeignClient.checkAndLock(commonStockLockVoList, orderNo);
            if (!isLockCommon){
                throw new SsyxException(ResultCodeEnum.ORDER_STOCK_FALL);
            }

        }

        //第四步,下单过程,操作数据库中两张表order_info 和 order_item 添加数据
        Long orderId = this.saveOrder(orderSubmitVo, cartInfoList);

        ////下订完成之后,要删除购物车中被选中的商品
        //先得到购物车中选中商品的skuId集合
        List<Long> skuIdList = cartInfoList.stream().map(CartInfo::getSkuId).collect(Collectors.toList());
        cartFeignClient.batchDeleteCart(skuIdList,userId);

        //第五步,返回订单id
        return orderId;
    }

    //操作数据库中两张表order_info 和 order_item 添加数据
    @Transactional(rollbackFor = {Exception.class})
    public Long saveOrder(OrderSubmitVo orderSubmitVo, List<CartInfo> cartInfoList) {
        Long userId = AuthContextHolder.getUserLoginVo().getUserId();
        if(CollectionUtils.isEmpty(cartInfoList)) {
            throw new SsyxException(ResultCodeEnum.DATA_ERROR);
        }
        //有户对应的提供点的团长相关信息
        LeaderAddressVo leaderAddressVo = userFeignClient.getUserAddressByUserId(userId);
        if(null == leaderAddressVo) {
            throw new SsyxException(ResultCodeEnum.DATA_ERROR);
        }

        //计算商品项分摊的优惠减少金额，按比例分摊，退款时按实际支付金额退款
        Map<String, BigDecimal> activitySplitAmountMap = this.computeActivitySplitAmount(cartInfoList);
        Map<String, BigDecimal> couponInfoSplitAmountMap = this.computeCouponInfoSplitAmount(cartInfoList, orderSubmitVo.getCouponId());
        //sku对应的订单明细(订单项item)
        //订单项表中的字段orderId,与订单表中的字段id是对应的,所以要先生成订单表中的id,然后再将此id属值给订单项表中的orderId
        List<OrderItem> orderItemList = new ArrayList<>();
        for (CartInfo cartInfo : cartInfoList) {
            OrderItem orderItem = new OrderItem();
            orderItem.setId(null);
            orderItem.setCategoryId(cartInfo.getCategoryId());
            if(cartInfo.getSkuType() == SkuType.COMMON.getCode()) {
                orderItem.setSkuType(SkuType.COMMON);
            } else {
                orderItem.setSkuType(SkuType.SECKILL);
            }
            orderItem.setSkuId(cartInfo.getSkuId());
            orderItem.setSkuName(cartInfo.getSkuName());
            orderItem.setSkuPrice(cartInfo.getCartPrice());
            orderItem.setImgUrl(cartInfo.getImgUrl());
            orderItem.setSkuNum(cartInfo.getSkuNum());
            orderItem.setLeaderId(orderSubmitVo.getLeaderId());
            //商品项促销活动分摊金额
            //key:"activity:"+第一个商品项的skuId
            BigDecimal splitActivityAmount = activitySplitAmountMap.get("activity:"+orderItem.getSkuId());
            if(null == splitActivityAmount) {
                splitActivityAmount = new BigDecimal(0);
            }
            orderItem.setSplitActivityAmount(splitActivityAmount);

            //商品项优惠券分摊金额(key同理)
            BigDecimal splitCouponAmount = couponInfoSplitAmountMap.get("coupon:"+orderItem.getSkuId());
            if(null == splitCouponAmount) {
                splitCouponAmount = new BigDecimal(0);
            }
            orderItem.setSplitCouponAmount(splitCouponAmount);

            //优惠后的总金额
            ////总价 = 商品单价*数量
            BigDecimal skuTotalAmount = orderItem.getSkuPrice().multiply(new BigDecimal(orderItem.getSkuNum()));
            ////最终总价 = 总价-促销活动分摊金额-优惠券分摊金额
            BigDecimal splitTotalAmount = skuTotalAmount.subtract(splitActivityAmount).subtract(splitCouponAmount);
            orderItem.setSplitTotalAmount(splitTotalAmount);
            orderItemList.add(orderItem);
        }

        //保存订单
        OrderInfo order = new OrderInfo();
        order.setUserId(userId);
        order.setOrderNo(orderSubmitVo.getOrderNo());
        order.setOrderStatus(OrderStatus.UNPAID);
        order.setProcessStatus(ProcessStatus.UNPAID);
        order.setCouponId(orderSubmitVo.getCouponId());
        order.setLeaderId(orderSubmitVo.getLeaderId());
        order.setLeaderName(leaderAddressVo.getLeaderName());
        order.setLeaderPhone(leaderAddressVo.getLeaderPhone());
        order.setTakeName(leaderAddressVo.getTakeName());
        order.setReceiverName(orderSubmitVo.getReceiverName());
        order.setReceiverPhone(orderSubmitVo.getReceiverPhone());
        order.setReceiverProvince(leaderAddressVo.getProvince());
        order.setReceiverCity(leaderAddressVo.getCity());
        order.setReceiverDistrict(leaderAddressVo.getDistrict());
        order.setReceiverAddress(leaderAddressVo.getDetailAddress());
        order.setWareId(cartInfoList.get(0).getWareId());

        //订单原总金额
        BigDecimal originalTotalAmount = this.computeTotalAmount(cartInfoList);

        //总订单促销活动分摊金额
        BigDecimal activityAmount = activitySplitAmountMap.get("activity:total");
        if(null == activityAmount) activityAmount = new BigDecimal(0);
        //总订单优惠券分摊金额
        BigDecimal couponAmount = couponInfoSplitAmountMap.get("coupon:total");
        if(null == couponAmount) couponAmount = new BigDecimal(0);
        //优惠后的总金额
        BigDecimal totalAmount = originalTotalAmount.subtract(activityAmount).subtract(couponAmount);
        //计算订单金额
        order.setOriginalTotalAmount(originalTotalAmount);//订单原总金额
        order.setActivityAmount(activityAmount);
        order.setCouponAmount(couponAmount);
        order.setTotalAmount(totalAmount);

        //计算团长佣金
        BigDecimal profitRate =  new BigDecimal(0); //orderSetService.getProfitRate();
        BigDecimal commissionAmount = order.getTotalAmount().multiply(profitRate);
        order.setCommissionAmount(commissionAmount);
        //保存订单
        baseMapper.insert(order);
        //保存订单后,表中自动生成订单号,再赋值给订单项表中的orderId
        for(OrderItem orderItem : orderItemList) {
            orderItem.setOrderId(order.getId());
        }
        orderItemService.saveBatch(orderItemList);

        ////订单生成之后,修改优惠券使用状态
        if(null != order.getCouponId()) {
            activityFeignClient.updateCouponInfoUseStatus(order.getCouponId(), order.getId(),userId);
        }

        //////下单成功，记录用户商品购买个数 String.valueOf(cartInfo.getSkuNum())
        //在redis中使用hash类型,key:userId,   field:skuId,    value:skuNum
        String orderSkuKey = "order:userId:"+userId;
        for(CartInfo cartInfo : cartInfoList){
            redisTemplate.opsForHash().put(orderSkuKey,String.valueOf(cartInfo.getSkuId()),String.valueOf(cartInfo.getSkuNum()));
        }
        //设置过期时间(过期时间到,当天晚上23:59:59)
        redisTemplate.expire(orderSkuKey, DateUtil.getCurrentExpireTimes(), TimeUnit.SECONDS);

        //返回订单号
        return order.getId();
    }

    //订算总订单金额
    private BigDecimal computeTotalAmount(List<CartInfo> cartInfoList) {
        BigDecimal total = new BigDecimal(0);
        for (CartInfo cartInfo : cartInfoList) {
            BigDecimal itemTotal = cartInfo.getCartPrice().multiply(new BigDecimal(cartInfo.getSkuNum()));
            total = total.add(itemTotal);
        }
        return total;
    }

    /**
     * 计算购物项分摊的优惠减少金额
     * 打折：按折扣分担
     * 现金：按比例分摊
     * @param cartInfoParamList
     * @return
     */
    private Map<String, BigDecimal> computeActivitySplitAmount(List<CartInfo> cartInfoParamList) {
        Map<String, BigDecimal> activitySplitAmountMap = new HashMap<>();

        //促销活动相关信息
        List<CartInfoVo> cartInfoVoList = activityFeignClient.findCartActivityList(cartInfoParamList);

        //活动总金额
        BigDecimal activityReduceAmount = new BigDecimal(0);
        if(!CollectionUtils.isEmpty(cartInfoVoList)) {
            for(CartInfoVo cartInfoVo : cartInfoVoList) {
                ActivityRule activityRule = cartInfoVo.getActivityRule();
                List<CartInfo> cartInfoList = cartInfoVo.getCartInfoList();
                if(null != activityRule) {
                    //优惠金额， 按比例分摊
                    BigDecimal reduceAmount = activityRule.getReduceAmount();
                    activityReduceAmount = activityReduceAmount.add(reduceAmount);
                    if(cartInfoList.size() == 1) {
                        //key:"activity:"+第一个商品项的skuId
                        activitySplitAmountMap.put("activity:"+cartInfoList.get(0).getSkuId(), reduceAmount);
                    } else {
                        //总金额
                        BigDecimal originalTotalAmount = new BigDecimal(0);
                        for(CartInfo cartInfo : cartInfoList) {
                            BigDecimal skuTotalAmount = cartInfo.getCartPrice().multiply(new BigDecimal(cartInfo.getSkuNum()));
                            originalTotalAmount = originalTotalAmount.add(skuTotalAmount);
                        }
                        //记录除最后一项是所有分摊金额， 最后一项=总的 - skuPartReduceAmount
                        BigDecimal skuPartReduceAmount = new BigDecimal(0);
                        if (activityRule.getActivityType() == ActivityType.FULL_REDUCTION) {
                            for(int i=0, len=cartInfoList.size(); i<len; i++) {
                                CartInfo cartInfo = cartInfoList.get(i);
                                if(i < len -1) {
                                    BigDecimal skuTotalAmount = cartInfo.getCartPrice().multiply(new BigDecimal(cartInfo.getSkuNum()));
                                    //sku分摊金额
                                    BigDecimal skuReduceAmount = skuTotalAmount.divide(originalTotalAmount, 2, RoundingMode.HALF_UP).multiply(reduceAmount);
                                    activitySplitAmountMap.put("activity:"+cartInfo.getSkuId(), skuReduceAmount);

                                    skuPartReduceAmount = skuPartReduceAmount.add(skuReduceAmount);
                                } else {
                                    BigDecimal skuReduceAmount = reduceAmount.subtract(skuPartReduceAmount);
                                    activitySplitAmountMap.put("activity:"+cartInfo.getSkuId(), skuReduceAmount);
                                }
                            }
                        } else {
                            for(int i=0, len=cartInfoList.size(); i<len; i++) {
                                CartInfo cartInfo = cartInfoList.get(i);
                                if(i < len -1) {
                                    BigDecimal skuTotalAmount = cartInfo.getCartPrice().multiply(new BigDecimal(cartInfo.getSkuNum()));

                                    //sku分摊金额
                                    BigDecimal skuDiscountTotalAmount = skuTotalAmount.multiply(activityRule.getBenefitDiscount().divide(new BigDecimal("10")));
                                    BigDecimal skuReduceAmount = skuTotalAmount.subtract(skuDiscountTotalAmount);
                                    activitySplitAmountMap.put("activity:"+cartInfo.getSkuId(), skuReduceAmount);

                                    skuPartReduceAmount = skuPartReduceAmount.add(skuReduceAmount);
                                } else {
                                    BigDecimal skuReduceAmount = reduceAmount.subtract(skuPartReduceAmount);
                                    activitySplitAmountMap.put("activity:"+cartInfo.getSkuId(), skuReduceAmount);
                                }
                            }
                        }
                    }
                }
            }
        }
        //优惠活动分摊总金额
        activitySplitAmountMap.put("activity:total", activityReduceAmount);
        return activitySplitAmountMap;
    }

    /**
     * 计算购物项分摊的优惠券减少金额
     * @param cartInfoList
     * @return
     */
    private Map<String, BigDecimal> computeCouponInfoSplitAmount(List<CartInfo> cartInfoList, Long couponId) {
        Map<String, BigDecimal> couponInfoSplitAmountMap = new HashMap<>();

        if(null == couponId) return couponInfoSplitAmountMap;
        CouponInfo couponInfo = activityFeignClient.findRangeSkuIdList(cartInfoList, couponId);

        if(null != couponInfo) {
            //sku对应的订单明细
            Map<Long, CartInfo> skuIdToCartInfoMap = new HashMap<>();
            for (CartInfo cartInfo : cartInfoList) {
                skuIdToCartInfoMap.put(cartInfo.getSkuId(), cartInfo);
            }
            //优惠券对应的skuId列表
            List<Long> skuIdList = couponInfo.getSkuIdList();
            if(CollectionUtils.isEmpty(skuIdList)) {
                return couponInfoSplitAmountMap;
            }
            //优惠券优化总金额
            BigDecimal reduceAmount = couponInfo.getAmount();
            if(skuIdList.size() == 1) {
                //sku的优化金额
                couponInfoSplitAmountMap.put("coupon:"+skuIdToCartInfoMap.get(skuIdList.get(0)).getSkuId(), reduceAmount);
            } else {
                //总金额
                BigDecimal originalTotalAmount = new BigDecimal(0);
                for (Long skuId : skuIdList) {
                    CartInfo cartInfo = skuIdToCartInfoMap.get(skuId);
                    BigDecimal skuTotalAmount = cartInfo.getCartPrice().multiply(new BigDecimal(cartInfo.getSkuNum()));
                    originalTotalAmount = originalTotalAmount.add(skuTotalAmount);
                }
                //记录除最后一项是所有分摊金额， 最后一项=总的 - skuPartReduceAmount
                BigDecimal skuPartReduceAmount = new BigDecimal(0);
                if (couponInfo.getCouponType() == CouponType.CASH || couponInfo.getCouponType() == CouponType.FULL_REDUCTION) {
                    for(int i=0, len=skuIdList.size(); i<len; i++) {
                        CartInfo cartInfo = skuIdToCartInfoMap.get(skuIdList.get(i));
                        if(i < len -1) {
                            BigDecimal skuTotalAmount = cartInfo.getCartPrice().multiply(new BigDecimal(cartInfo.getSkuNum()));
                            //sku分摊金额
                            BigDecimal skuReduceAmount = skuTotalAmount.divide(originalTotalAmount, 2, RoundingMode.HALF_UP).multiply(reduceAmount);
                            couponInfoSplitAmountMap.put("coupon:"+cartInfo.getSkuId(), skuReduceAmount);

                            skuPartReduceAmount = skuPartReduceAmount.add(skuReduceAmount);
                        } else {
                            BigDecimal skuReduceAmount = reduceAmount.subtract(skuPartReduceAmount);
                            couponInfoSplitAmountMap.put("coupon:"+cartInfo.getSkuId(), skuReduceAmount);
                        }
                    }
                }
            }
            couponInfoSplitAmountMap.put("coupon:total", couponInfo.getAmount());
        }
        return couponInfoSplitAmountMap;
    }

    //获取订单详情
    @Override
    public OrderInfo getOrderInfoById(Long orderId) {

        //根据orderId查询订单基本信息
        OrderInfo orderInfo = baseMapper.selectById(orderId);

        //加入订单状态
        orderInfo.getParam().put("orderStatusName", orderInfo.getOrderStatus().getComment());

        //根据orderId查询订单所有订单项item列表
        List<OrderItem> orderItemList = orderItemService.list(new LambdaQueryWrapper<OrderItem>()
                                                                .eq(OrderItem::getOrderId, orderId));
        //查询所有订单项封装到每个订单对象中
        orderInfo.setOrderItemList(orderItemList);
        return orderInfo;
    }

    //根据orderNo,查询订单相关信息
    @Override
    public OrderInfo getOrderInfoByOrderNo(String orderNo) {
        LambdaQueryWrapper<OrderInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(OrderInfo::getOrderNo,orderNo);
        OrderInfo orderInfo = baseMapper.selectOne(wrapper);
        return orderInfo;
    }

    //修改订单信息表状态,已支付后的状态
    @Override
    public void orderPay(String orderNo) {
        //查询订单状态是否已经修改完成成了支付状态
        OrderInfo orderInfo = this.getOrderInfoByOrderNo(orderNo);
        if(orderInfo == null || orderInfo.getOrderStatus() != OrderStatus.UNPAID){
            return;
        }
        //如果没有修改支付状态,则修改
        orderInfo.setOrderStatus(OrderStatus.WAITING_DELEVER);
        orderInfo.setProcessStatus(ProcessStatus.WAITING_DELEVER);
        orderInfo.setPaymentTime(new Date());
        baseMapper.updateById(orderInfo);
    }

    //获取用户订单分页列表
    @Override
    public IPage<OrderInfo> findUserOrderPage(Page<OrderInfo> pageParam, OrderUserQueryVo orderUserQueryVo) {

        LambdaQueryWrapper<OrderInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(OrderInfo::getUserId,orderUserQueryVo.getUserId())
                .eq(OrderInfo::getOrderStatus,orderUserQueryVo.getOrderStatus());
        IPage<OrderInfo> pageModel = baseMapper.selectPage(pageParam, wrapper);
        List<OrderInfo> orderInfoList = pageModel.getRecords();
        //获取每个订单，把每个订单里面订单项查询封装
        for(OrderInfo orderInfo : orderInfoList) {
            //根据订单id查询里面所有订单项列表
            List<OrderItem> orderItemList = orderItemService.list(
                    new LambdaQueryWrapper<OrderItem>()
                            .eq(OrderItem::getOrderId, orderInfo.getId())
            );
            //把订单项集合封装到每个订单里面
            orderInfo.setOrderItemList(orderItemList);
            //封装订单状态名称
            orderInfo.getParam().put("orderStatusName",orderInfo.getOrderStatus().getComment());
        }
        return pageModel;
    }
}
